import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
  HttpStatus,
  HttpException
} from '@nestjs/common'
import { Request } from 'express'
import { Reflector } from '@nestjs/core'
import { JwtService } from '@nestjs/jwt'
import { IS_PUBLIC_KEY, JWT_SECRET_KEY, CHECK_PERMISSION } from 'config'
import { RoleService } from 'src/auth/role/role.service'
import { UserService } from 'src/user/user.service'
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private readonly jwtService: JwtService,
    private readonly reflector: Reflector,
    private readonly roleService: RoleService,
    private readonly userService: UserService
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 检查是否被 @Public 装饰器标记
    const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass()
    ])
    const checkPermission = this.reflector.getAllAndOverride(CHECK_PERMISSION, [
      context.getHandler(),
      context.getClass()
    ])
    if (isPublic) return true

    const request = context.switchToHttp().getRequest<Request>()

    /**
     * 检查请求参数中是否携带filterToken，有则直接放行
     */
    if (request.query['filterToken'] === JWT_SECRET_KEY) return true

    const token = this.extractTokenFromHeader(request)

    if (!token) {
      throw new UnauthorizedException('用户未登录')
    }
    try {
      const payload = await this.jwtService.verifyAsync(token, {
        secret: JWT_SECRET_KEY
      })

      let userInfo = request['userInfo']
      if (checkPermission) {
        if (!request['userInfo']) {
          userInfo = await this.userService.findOne(payload.userId)
          request['userInfo'] = userInfo
        }

        if (userInfo.roleId && userInfo.roleCode === 'Administrator') {
          request['permissions'] = 'Administrator'
        } else {
          const permissions = await this.roleService.getPermission(
            userInfo.roleId
          )
          request['permissions'] = permissions
          // 获取当前请求的完整路径
          const fullPath = this.getFullPath(request)
          console.log('fullPath', fullPath)

          // 检查接口权限
          if (!this.hasInterfacePermission(fullPath, request['permissions'])) {
            throw new HttpException('接口权限不足', HttpStatus.FORBIDDEN)
          }
        }
      }
      request['user'] = { ...payload, ...userInfo }

      return true
    } catch (error) {
      if (error instanceof HttpException) {
        throw error // 重新抛出已有的HttpException
      }
      console.log('error 用户未登录', error)
      throw new UnauthorizedException('用户未登录')
    }
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers['authorization']?.split(' ') ?? []
    if (type && type !== 'Bearer')
      throw new HttpException(
        'Invalid authorization type',
        HttpStatus.UNAUTHORIZED
      )

    return type === 'Bearer' ? token : undefined
  }

  private getFullPath(request: Request): string {
    // 构造完整的请求路径，包括HTTP方法和路径
    return `${request.method.toLowerCase()}:${request.path}`
  }

  private hasInterfacePermission(
    requestPath: string,
    permissions: string[]
  ): boolean {
    // 如果权限数组为空，则没有权限
    if (!Array.isArray(permissions) || permissions.length === 0) {
      return false
    }

    // 直接匹配
    if (permissions.includes(requestPath)) {
      return true
    }

    // 路径参数匹配，例如 "/auth/role/:id" 可以匹配 "/auth/role/1"
    for (const permission of permissions) {
      if (this.matchPathWithParams(permission, requestPath)) {
        return true
      }
    }

    // 通配符匹配，例如 "/api/users/*" 可以匹配 "/api/users/1"
    for (const permission of permissions) {
      if (permission.endsWith('/*')) {
        const prefix = permission.slice(0, -1) // 移除 '*'
        if (requestPath.startsWith(prefix)) {
          return true
        }
      }
    }

    // 全局权限匹配
    if (permissions.includes('*')) {
      return true
    }

    return false
  }

  /**
   * @description 匹配带参数的路径
   * @param pattern 权限配置路径，如 /auth/role/:id
   * @param path 实际请求路径，如 /auth/role/1
   * @returns boolean 是否匹配
   */
  private matchPathWithParams(pattern: string, path: string): boolean {
    // 处理带参数的路径匹配
    if (pattern.includes(':')) {
      // 将权限路径转换为正则表达式
      const patternParts = pattern.split('/')
      const pathParts = path.split('/')

      // 如果路径部分数量不一致，直接返回false
      if (patternParts.length !== pathParts.length) {
        return false
      }

      // 逐个比较路径部分
      for (let i = 0; i < patternParts.length; i++) {
        // 如果是参数占位符（以:开头），跳过检查
        if (patternParts[i].startsWith(':')) {
          // 可以添加额外的参数验证逻辑
          // 例如验证是否为数字: /^\d+$/.test(pathParts[i])
          continue
        }

        // 如果不是参数占位符，需要完全匹配
        if (patternParts[i] !== pathParts[i]) {
          return false
        }
      }

      return true
    }

    return false
  }
}
